package agent.model.royaume;

import ihm.MainFrame.IHM;
import jade.core.Agent;
import jade.wrapper.AgentContainer;
import jade.wrapper.AgentController;
import jade.wrapper.StaleProxyException;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map.Entry;

import util.Constant;
import agent.behaviour.ReadReportBehaviour;
import agent.enums.Ressource;
import agent.enums.TypeAgent;

/**
 * Agent permetant de gérer les agents du container et de récolter des données liées à leur comportement.
 * @author Jean-François Augé & Guillaume Poças
 */
public class AgentRoyaume extends Agent {

	private static final long serialVersionUID = 1L;

	//private int tempsCycle = 10000;
	private int nombreCycle = 3000;
	private int currentCycle = 0;

	private AgentContainer container;
	private ArrayList<AgentController> listeAgents;
	private int[] nombreAgents = {0, 0, 0, 0, 0, 0, 0, 0}; //rajout de 3 col debug ABC
	//private boolean deadRoyaume;

	private IHM ihm;

	//private HashMap<String, String> map = new HashMap<String, String>();
	private HashMap<String, double[]> map = new HashMap<String, double[]>();

	/**
	 * Créer les agents de départ et lancer l'IHM.
	 */
	@Override
	protected void setup() {
		container = getContainerController();
		listeAgents = new ArrayList<>();
		//deadRoyaume = false;

		try {
			creeAgent(TypeAgent.Bijoutier);
			creeAgent(TypeAgent.Citoyen);
			creeAgent(TypeAgent.Forgeron);
			creeAgent(TypeAgent.Mineur, Ressource.Metal);
			creeAgent(TypeAgent.Mineur, Ressource.Pierre);
			creeAgent(TypeAgent.Roi);

			//debug agents A B C
			/*creeAgent(TypeAgent.A);
			creeAgent(TypeAgent.B);
			creeAgent(TypeAgent.C);*/

			for(AgentController ac : listeAgents)
				ac.start();
		} catch (StaleProxyException e) {
			e.printStackTrace();
		}

		addBehaviour(new ReadReportBehaviour(this));

		ihm = new IHM(this);
		ihm.start();
	}

	/**
	 * Créer un nouvel agent.
	 * @param type
	 * @param ressources
	 * @throws StaleProxyException
	 */
	public void creeAgent(TypeAgent type, Ressource...ressources) throws StaleProxyException {
		Object[] args = new Object[17];
		AgentController agent = null;

		args[0] = this;
		if(ressources.length > 0) args[1] = ressources[0];

		//tous les cas non traités, on utilise la Constante établie
		for(int i = 0; i < args.length; i++)
			switch(i){
			case 2:
				args[i] = Constant.defaultTempsCycle;
				break;
			case 3:
				args[i] = Constant.defaultCycleMax;
				break;
			case 4:
				args[i] = Constant.defaultPrix;
				break;
			case 5:
				args[i] = Constant.defaultRythmeProduction;
				break;
			case 6:
				args[i] = Constant.defaultStockProductionCourant;
				break;
			case 7:
				args[i] = Constant.defaultStockProduitMax;
				break;
			case 8:
				args[i] = Constant.defaultRythmeConsommation;
				break;
			case 9:
				args[i] = Constant.defaultStockConsommationCourant;
				break;
			case 10:
				args[i] = Constant.defaultStockConsommeMax;
				break;
			case 11:
				args[i] = Constant.defaultArgent;
				break;
			case 12:
				args[i] = Constant.defaultSatisfactionHaut;
				break;
			case 13:
				args[i] = Constant.defaultSatisfactionBas;
				break;
			case 14:
				args[i] = Constant.defaultSatisfaction;
				break;
			case 15:
				args[i] = Constant.defaultFrequenceImpots;
				break;
			case 16:
				args[i] = Constant.defaultTauxImpots;
				break;
			default:
				break;
			}

		switch(type.toString()) {
		case "Bijoutier":
			nombreAgents[0]++;
			agent = container.createNewAgent(type.toString()+"_"+nombreAgents[0], "agent.model.royaume.Agent"+type.toString(), args);
			break;
		case "Citoyen":
			nombreAgents[1]++;
			agent = container.createNewAgent(type.toString()+"_"+nombreAgents[1], "agent.model.royaume.Agent"+type.toString(), args);
			break;
		case "Forgeron":
			nombreAgents[2]++;
			agent = container.createNewAgent(type.toString()+"_"+nombreAgents[2], "agent.model.royaume.Agent"+type.toString(), args);
			break;
		case "Mineur":
			nombreAgents[3]++;
			agent = container.createNewAgent(type.toString()+"_"+nombreAgents[3], "agent.model.royaume.Agent"+type.toString(), args);
			break;
		case "Roi":
			nombreAgents[4]++;
			agent = container.createNewAgent(type.toString()+"_"+nombreAgents[4], "agent.model.royaume.Agent"+type.toString(), args);
			break;
		case "A":
			nombreAgents[5]++;
			agent = container.createNewAgent(type.toString()+"_"+nombreAgents[5], "agent.model.simple.Agent"+type.toString(), args);
			break;
		case "B":
			nombreAgents[6]++;
			agent = container.createNewAgent(type.toString()+"_"+nombreAgents[6], "agent.model.simple.Agent"+type.toString(), args);
			break;
		case "C":
			nombreAgents[7]++;
			agent = container.createNewAgent(type.toString()+"_"+nombreAgents[7], "agent.model.simple.Agent"+type.toString(), args);
			break;
		default:
			break;
		}

		listeAgents.add(agent);
	}

	/**
	 * Créer un nouvel agent au cours de la simulation.
	 * @param type
	 * @param param
	 * @param ressources
	 * @throws StaleProxyException
	 */
	public void creeAgentParameters(TypeAgent type, ArrayList<String> param, Ressource...ressources) throws StaleProxyException {
		int taille = 17; //faux, nombre d'arguments dans AbstractAgent
		Object[] args = new Object[taille];
		AgentController agent = null;

		//on indique default à tous les params
		for(int i = 0; i < args.length; i++)
			args[i] = "default";

		//on ajoute le royaume
		args[0] = this;
		//on indique la ressource s'il y en a une
		if(ressources.length > 0)	args[1] = ressources[0];

		//0 = Royaume, 1 = Ressource (optionnel)
		//2 = tempsCycle, 3 = nombreCycles, 4 = prix
		//5 = rythmeProduction, 6 = stockProductionCourant, 7 = stockProduitMax
		//8 = rythmeConsommation, 9 = stockConsommationCourant, 10 = stockConsommeMax
		//11 = argent, 12 = satisfactionHaut, 13 = satisfactionBas, 14 = satisfaction
		//15 = frequenceImpots, 16 = tauxImpots

		//parcours de l'arraylist
		for(int i = 0; i < param.size(); i++)
			//traiter que les param pairs
			if((i%2) == 0)
				//si les param correspondent, le suivant dans la liste est sa valeur
				switch(param.get(i)) {
				case "-tempsCycle":
					args[2] = Integer.parseInt(param.get(i+1));
					break;
				case "-nombreCycles":
					args[3] = Integer.parseInt(param.get(i+1));
					break;
				case "-prix":
					args[4] = Double.parseDouble(param.get(i+1));
					break;
				case "-rythmeProduction":
					args[5] = Double.parseDouble(param.get(i+1));
					break;
				case "-stockProductionCourant":
					args[6] = Double.parseDouble(param.get(i+1));
					break;
				case "-stockProduitMax":
					args[7] = Integer.parseInt(param.get(i+1));
					break;
				case "-rythmeConsommation":
					args[8] = Double.parseDouble(param.get(i+1));
					break;
				case "-stockConsommationCourant":
					args[9] = Double.parseDouble(param.get(i+1));
					break;
				case "-stockConsommeMax":
					args[10] = Integer.parseInt(param.get(i+1));
					break;
				case "-argent":
					args[11] = Double.parseDouble(param.get(i+1));
					break;
				case "-satisfactionHaut":
					args[12] = Double.parseDouble(param.get(i+1));
					break;
				case "-satisfactionBas":
					args[13] = Double.parseDouble(param.get(i+1));
					break;
				case "-satisfaction":
					args[14] = Double.parseDouble(param.get(i+1));
					break;
				case "-frequenceImpots":
					args[15] = Double.parseDouble(param.get(i+1));
					break;
				case "-tauxImpots":
					args[16] = Double.parseDouble(param.get(i+1));
					break;
				default:
					break;
				}

		//tous les cas non traités, on utilise la Constante établie
		for(int i = 0; i < args.length; i++)
			if(args[i].equals("default"))
				switch(i){
				case 2:
					args[i] = Constant.defaultTempsCycle;
					break;
				case 3:
					args[i] = Constant.defaultCycleMax;
					break;
				case 4:
					args[i] = Constant.defaultPrix;
					break;
				case 5:
					args[i] = Constant.defaultRythmeProduction;
					break;
				case 6:
					args[i] = Constant.defaultStockProductionCourant;
					break;
				case 7:
					args[i] = Constant.defaultStockProduitMax;
					break;
				case 8:
					args[i] = Constant.defaultRythmeConsommation;
					break;
				case 9:
					args[i] = Constant.defaultStockConsommationCourant;
					break;
				case 10:
					args[i] = Constant.defaultStockConsommeMax;
					break;
				case 11:
					args[i] = Constant.defaultArgent;
					break;
				case 12:
					args[i] = Constant.defaultSatisfactionHaut;
					break;
				case 13:
					args[i] = Constant.defaultSatisfactionBas;
					break;
				case 14:
					args[i] = Constant.defaultSatisfaction;
					break;
				case 15:
					args[i] = Constant.defaultFrequenceImpots;
					break;
				case 16:
					args[i] = Constant.defaultTauxImpots;
					break;
				default:
					break;
				}

		switch(type.toString()) {
		case "Bijoutier":
			nombreAgents[0]++;
			agent = container.createNewAgent(type.toString()+"_"+nombreAgents[0], "agent.model.royaume.Agent"+type.toString(), args);
			break;
		case "Citoyen":
			nombreAgents[1]++;
			agent = container.createNewAgent(type.toString()+"_"+nombreAgents[1], "agent.model.royaume.Agent"+type.toString(), args);
			break;
		case "Forgeron":
			nombreAgents[2]++;
			agent = container.createNewAgent(type.toString()+"_"+nombreAgents[2], "agent.model.royaume.Agent"+type.toString(), args);
			break;
		case "Mineur":
			nombreAgents[3]++;
			agent = container.createNewAgent(type.toString()+"_"+nombreAgents[3], "agent.model.royaume.Agent"+type.toString(), args);
			break;
		case "Roi":
			nombreAgents[4]++;
			agent = container.createNewAgent(type.toString()+"_"+nombreAgents[4], "agent.model.royaume.Agent"+type.toString(), args);
			break;
		case "A":
			nombreAgents[5]++;
			agent = container.createNewAgent(type.toString()+"_"+nombreAgents[5], "agent.model.simple.Agent"+type.toString(), args);
			break;
		case "B":
			nombreAgents[6]++;
			agent = container.createNewAgent(type.toString()+"_"+nombreAgents[6], "agent.model.simple.Agent"+type.toString(), args);
			break;
		case "C":
			nombreAgents[7]++;
			agent = container.createNewAgent(type.toString()+"_"+nombreAgents[7], "agent.model.simple.Agent"+type.toString(), args);
			break;
		default:
			break;
		}

		listeAgents.add(agent);

		agent.start();
	}


	/**
	 * Méthode de debug.
	 */
	public void printMap() {
		for(Entry<String, double[]> e : map.entrySet()) {
			System.out.print(e.getKey()+" = ");
			for(double d : e.getValue())
				System.out.print(d+" ");
			System.out.println("");
		}
		System.out.println();
	}

	public HashMap<String, double[]> getMap() {
		return map;
	}

	public void setMap(HashMap<String, double[]> map) {
		this.map = map;
	}

	public int[] getNombreAgents() {
		return nombreAgents;
	}

	public void setNombreAgents(int[] nombreAgents) {
		this.nombreAgents = nombreAgents;
	}

	public void decreaseNombreAgents(String agentType) {
		int agentNum = 100;
		switch(agentType) {
		case "Bijoutier":
			agentNum = 0;
			break;
		case "Citoyen":
			agentNum = 1;
			break;
		case "Forgeron":
			agentNum = 2;
			break;
		case "Mineur":
			agentNum = 3;
			break;
		case "Roi":
			agentNum = 4;
			break;
		case "A":
			agentNum = 5;
			break;
		case "B":
			agentNum = 6;
			break;
		case "C":
			agentNum = 7;
			break;
		default:
			break;
		}

		nombreAgents[agentNum]--;
		System.out.println("Decrease agent "+agentType);
	}

	public void increaseCurrentCycle() {
		currentCycle++;
	}

	public boolean isDeadRoyaume() {
		for(int i = 0; i < nombreAgents.length; i++)
			//on compte tous les agents, sauf le Roi
			if((nombreAgents[i] != 0) && (i != 4))	return false;
		System.out.println("Tous les agents sont morts\nFin de la simulation après "+currentCycle+" cycles.");
		return true;
	}

	/*public void setDeadRoyaume(boolean deadRoyaume) {
		this.deadRoyaume = deadRoyaume;
	}*/

	public boolean isEndOfCycles() {
		if(currentCycle == nombreCycle) {
			System.out.println("Fin du temps!\nFin de la simulation après "+currentCycle+" cycles.");
			return true;
		}
		else	return false;
	}

	public int getCurrentCycle() {
		return currentCycle;
	}

}
